/*-------------------------------------------------------------------------
 *
 * pg_aggregate.c
 *	  支持pg_aggregate关系操作的常规例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/pg_aggregate.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_language.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"


static Oid	fc_lookup_agg_function(List *fc_fnName, int fc_nargs, Oid *fc_input_types,
								Oid fc_variadicArgType,
								Oid *fc_rettype);


/*
 * AggregateCreate
 */
ObjectAddress AggregateCreate(const char *fc_aggName,
				Oid fc_aggNamespace,
				bool fc_replace,
				char fc_aggKind,
				int fc_numArgs,
				int fc_numDirectArgs,
				oidvector *fc_parameterTypes,
				Datum fc_allParameterTypes,
				Datum fc_parameterModes,
				Datum fc_parameterNames,
				List *fc_parameterDefaults,
				Oid fc_variadicArgType,
				List *fc_aggtransfnName,
				List *fc_aggfinalfnName,
				List *fc_aggcombinefnName,
				List *fc_aggserialfnName,
				List *fc_aggdeserialfnName,
				List *fc_aggmtransfnName,
				List *fc_aggminvtransfnName,
				List *fc_aggmfinalfnName,
				bool fc_finalfnExtraArgs,
				bool fc_mfinalfnExtraArgs,
				char fc_finalfnModify,
				char fc_mfinalfnModify,
				List *fc_aggsortopName,
				Oid fc_aggTransType,
				int32 fc_aggTransSpace,
				Oid fc_aggmTransType,
				int32 fc_aggmTransSpace,
				const char *fc_agginitval,
				const char *fc_aggminitval,
				char fc_proparallel)
{
	Relation	fc_aggdesc;
	HeapTuple	fc_tup;
	HeapTuple	fc_oldtup;
	bool		fc_nulls[Natts_pg_aggregate];
	Datum		fc_values[Natts_pg_aggregate];
	bool		fc_replaces[Natts_pg_aggregate];
	Form_pg_proc fc_proc;
	Oid			fc_transfn;
	Oid			fc_finalfn = InvalidOid;	/* 可以省略 */
	Oid			fc_combinefn = InvalidOid; /* 可以省略 */
	Oid			fc_serialfn = InvalidOid;	/* 可以省略 */
	Oid			fc_deserialfn = InvalidOid;	/* 可以省略 */
	Oid			fc_mtransfn = InvalidOid;	/* 可以省略 */
	Oid			fc_minvtransfn = InvalidOid;	/* 可以省略 */
	Oid			fc_mfinalfn = InvalidOid;	/* 可以省略 */
	Oid			fc_sortop = InvalidOid;	/* 可以省略 */
	Oid		   *fc_aggArgTypes = fc_parameterTypes->values;
	bool		fc_mtransIsStrict = false;
	Oid			fc_rettype;
	Oid			fc_finaltype;
	Oid			fc_fnArgs[FUNC_MAX_ARGS];
	int			fc_nargs_transfn;
	int			fc_nargs_finalfn;
	Oid			fc_procOid;
	TupleDesc	fc_tupDesc;
	char	   *fc_detailmsg;
	int			fc_i;
	ObjectAddress fc_myself,
				fc_referenced;
	ObjectAddresses *fc_addrs;
	AclResult	fc_aclresult;

	/* 完整性检查（调用者应已捕获这些） */
	if (!fc_aggName)
		elog(ERROR, "no aggregate name supplied");

	if (!fc_aggtransfnName)
		elog(ERROR, "aggregate must have a transition function");

	if (fc_numDirectArgs < 0 || fc_numDirectArgs > fc_numArgs)
		elog(ERROR, "incorrect number of direct arguments for aggregate");

	/*
	 * 聚合最多可以有 FUNC_MAX_ARGS-1 个参数，否则 transfn
	 * 和/或 finalfn 将无法在 pg_proc 中表示。我们必须现在进行检查
	 * 以保护固定大小的数组以及可能在被调用的函数中。
	 */
	if (fc_numArgs < 0 || fc_numArgs > FUNC_MAX_ARGS - 1)
		ereport(ERROR,
				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
				 errmsg_plural("aggregates cannot have more than %d argument",
							   "aggregates cannot have more than %d arguments",
							   FUNC_MAX_ARGS - 1,
							   FUNC_MAX_ARGS - 1)));

	/*
	 * 如果 transtype 是多态的，也必须有多态参数；否则
	 * 我们将没有办法推断实际的 transtype。
	 */
	fc_detailmsg = check_valid_polymorphic_signature(fc_aggTransType,
												  fc_aggArgTypes,
												  fc_numArgs);
	if (fc_detailmsg)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("cannot determine transition data type"),
				 errdetail_internal("%s", fc_detailmsg)));

	/*
	 * 对于移动聚合的 transtype 也同样适用（如果有的话）
	 */
	if (OidIsValid(fc_aggmTransType))
	{
		fc_detailmsg = check_valid_polymorphic_signature(fc_aggmTransType,
													  fc_aggArgTypes,
													  fc_numArgs);
		if (fc_detailmsg)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("cannot determine transition data type"),
					 errdetail_internal("%s", fc_detailmsg)));
	}

	/*
	 * 一个有序集合聚合，如果是 VARIADIC，必须是 VARIADIC ANY。
	 * 原则上，我们可以支持常规可变参数类型，但这会使事情
	 * 变得更加复杂，因为我们必须将正确的参数子集组装成数组值。
	 * 由于没有标准聚合需要这种情况，因此我们现在不去处理它。
	 */
	if (AGGKIND_IS_ORDERED_SET(fc_aggKind) && OidIsValid(fc_variadicArgType) &&
		fc_variadicArgType != ANYOID)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("a variadic ordered-set aggregate must use VARIADIC type ANY")));

	/*
	 * 如果它是一个假想集合聚合，必须至少有与聚合的参数
	 * 一样多的直接参数，并且最后 N 个直接参数
	 * 必须在类型上与聚合参数匹配。（我们在调用聚合
	 * 时必须再次检查这一点，以防 ANY 被涉及，但如果声明的
	 * 参数类型不匹配，现在拒绝聚合定义是有意义的。）
	 * 如果 numDirectArgs == numArgs，则无条件OK，这表明
	 * 语法将两个列表中的相同 VARIADIC 条目合并。否则，
	 * 如果 agg 是 VARIADIC，则聚合的一侧只能是 VARIADIC，
	 * 这是不允许的。否则，请坚持确保每一侧的最后
	 * N 个参数类型完全匹配。
	 */
	if (fc_aggKind == AGGKIND_HYPOTHETICAL &&
		fc_numDirectArgs < fc_numArgs)
	{
		int			fc_numAggregatedArgs = fc_numArgs - fc_numDirectArgs;

		if (OidIsValid(fc_variadicArgType) ||
			fc_numDirectArgs < fc_numAggregatedArgs ||
			memcmp(fc_aggArgTypes + (fc_numDirectArgs - fc_numAggregatedArgs),
				   fc_aggArgTypes + fc_numDirectArgs,
				   fc_numAggregatedArgs * sizeof(Oid)) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("a hypothetical-set aggregate must have direct arguments matching its aggregated arguments")));
	}

	/*
	 * 查找 transfn。对于普通聚合，它接受 transtype 加上所有
	 * 聚合参数。对于有序集合聚合，它接受 transtype 加上
	 * 所有聚合参数，但不包括直接参数。然而，我们必须
	 * 特别处理一个尾随 VARIADIC 项的情况，它被视为
	 * 同时覆盖直接参数和聚合参数。
	 */
	if (AGGKIND_IS_ORDERED_SET(fc_aggKind))
	{
		if (fc_numDirectArgs < fc_numArgs)
			fc_nargs_transfn = fc_numArgs - fc_numDirectArgs + 1;
		else
		{
			/* 特殊情况，最后参数为 VARIADIC */
			Assert(fc_variadicArgType != InvalidOid);
			fc_nargs_transfn = 2;
		}
		fc_fnArgs[0] = fc_aggTransType;
		memcpy(fc_fnArgs + 1, fc_aggArgTypes + (fc_numArgs - (fc_nargs_transfn - 1)),
			   (fc_nargs_transfn - 1) * sizeof(Oid));
	}
	else
	{
		fc_nargs_transfn = fc_numArgs + 1;
		fc_fnArgs[0] = fc_aggTransType;
		memcpy(fc_fnArgs + 1, fc_aggArgTypes, fc_numArgs * sizeof(Oid));
	}
	fc_transfn = fc_lookup_agg_function(fc_aggtransfnName, fc_nargs_transfn,
								  fc_fnArgs, fc_variadicArgType,
								  &fc_rettype);

	/*
	 * transfn 的返回类型（可能在 enforce_generic_type_consistency
	 * 的细化之后，如果 transtype 不是多态的）必须
	 * 完全匹配声明的 transtype。
	 *
	 * 在非多态的 transtype 情况下，允许一个
	 * 可以转换为 transtype 的返回类型可能是可以的，
	 * 但我不太确信这是否安全或有用。当 transtype 是
	 * 多态时，我们 *必须* 要求完全相等。
	 */
	if (fc_rettype != fc_aggTransType)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("return type of transition function %s is not %s",
						NameListToString(fc_aggtransfnName),
						format_type_be(fc_aggTransType))));

	fc_tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_transfn));
	if (!HeapTupleIsValid(fc_tup))
		elog(ERROR, "cache lookup failed for function %u", fc_transfn);
	fc_proc = (Form_pg_proc) GETSTRUCT(fc_tup);

	/*
	 * 如果 transfn 是严格的，并且 initval 为 NULL，确保第一个输入
	 * 类型和 transtype 相同（或至少是二进制兼容的），这样
	 * 就可以使用第一个输入值作为初始 transValue。
	 */
	if (fc_proc->proisstrict && fc_agginitval == NULL)
	{
		if (fc_numArgs < 1 ||
			!IsBinaryCoercible(fc_aggArgTypes[0], fc_aggTransType))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
	}

	ReleaseSysCache(fc_tup);

	/* 处理提供的移动聚合 transfn */
	if (fc_aggmtransfnName)
	{
		/*
		 * 参数与常规 transfn 相同，除了转换数据类型可能不同。
		 * 所以重用上面设置的 fnArgs 值，除了那个。
		 */
		Assert(OidIsValid(fc_aggmTransType));
		fc_fnArgs[0] = fc_aggmTransType;

		fc_mtransfn = fc_lookup_agg_function(fc_aggmtransfnName, fc_nargs_transfn,
									   fc_fnArgs, fc_variadicArgType,
									   &fc_rettype);

		/* 与上面一样，返回类型必须完全匹配声明的 mtranstype。 */
		if (fc_rettype != fc_aggmTransType)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("return type of transition function %s is not %s",
							NameListToString(fc_aggmtransfnName),
							format_type_be(fc_aggmTransType))));

		fc_tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_mtransfn));
		if (!HeapTupleIsValid(fc_tup))
			elog(ERROR, "cache lookup failed for function %u", fc_mtransfn);
		fc_proc = (Form_pg_proc) GETSTRUCT(fc_tup);

		/*
		 * 如果 mtransfn 是严格的，并且 minitval 为 NULL，检查第一个
		 * 输入类型和 mtranstype 是否二进制兼容。
		 */
		if (fc_proc->proisstrict && fc_aggminitval == NULL)
		{
			if (fc_numArgs < 1 ||
				!IsBinaryCoercible(fc_aggArgTypes[0], fc_aggmTransType))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
		}

		/* 记住 mtransfn 是否严格；我们可能需要在下面用到它 */
		fc_mtransIsStrict = fc_proc->proisstrict;

		ReleaseSysCache(fc_tup);
	}

	/* 处理提供的 minvtransfn */
	if (fc_aggminvtransfnName)
	{
		/*
		 * 必须与前向转换函数具有相同数量和类型的参数，
		 * 因此只需重用 fnArgs 数据。
		 */
		Assert(fc_aggmtransfnName);

		fc_minvtransfn = fc_lookup_agg_function(fc_aggminvtransfnName, fc_nargs_transfn,
										  fc_fnArgs, fc_variadicArgType,
										  &fc_rettype);

		/* 与上面一样，返回类型必须完全匹配声明的 mtranstype。 */
		if (fc_rettype != fc_aggmTransType)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("return type of inverse transition function %s is not %s",
							NameListToString(fc_aggminvtransfnName),
							format_type_be(fc_aggmTransType))));

		fc_tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_minvtransfn));
		if (!HeapTupleIsValid(fc_tup))
			elog(ERROR, "cache lookup failed for function %u", fc_minvtransfn);
		fc_proc = (Form_pg_proc) GETSTRUCT(fc_tup);

		/*
		 * 我们要求前向和反向转换函数的严格性设置必须一致。
		 * 这可以避免在执行时处理各种特殊情况。
		 */
		if (fc_proc->proisstrict != fc_mtransIsStrict)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("strictness of aggregate's forward and inverse transition functions must match")));

		ReleaseSysCache(fc_tup);
	}

	/* 处理提供的 finalfn */
	if (fc_aggfinalfnName)
	{
		/*
		 * 如果指定了 finalfnExtraArgs，transfn 接受 transtype
		 * 加上所有参数；否则，它只接受 transtype 加上任何
		 * 直接参数。（非直接参数在运行时是无用的，
		 * 实际上作为 NULL 传递，但我们可能需要它们在函数
		 * 签名中，以允许解决多态聚合的结果类型。）
		 */
		Oid			fc_ffnVariadicArgType = fc_variadicArgType;

		fc_fnArgs[0] = fc_aggTransType;
		memcpy(fc_fnArgs + 1, fc_aggArgTypes, fc_numArgs * sizeof(Oid));
		if (fc_finalfnExtraArgs)
			fc_nargs_finalfn = fc_numArgs + 1;
		else
		{
			fc_nargs_finalfn = fc_numDirectArgs + 1;
			if (fc_numDirectArgs < fc_numArgs)
			{
				/* 可变参数不影响 finalfn */
				fc_ffnVariadicArgType = InvalidOid;
			}
		}

		fc_finalfn = fc_lookup_agg_function(fc_aggfinalfnName, fc_nargs_finalfn,
									  fc_fnArgs, fc_ffnVariadicArgType,
									  &fc_finaltype);

		/*
		 * 当指定了 finalfnExtraArgs 时，finalfn 至少将接收到
		 * 一个空参数，因此如果它是严格的，请进行抱怨。
		 * 在运行时不会发生任何坏事（你只会得到一个空结果），
		 * 但这显然不是用户想要的，因此我们现在要抱怨。
		 */
		if (fc_finalfnExtraArgs && func_strict(fc_finalfn))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("final function with extra arguments must not be declared STRICT")));
	}
	else
	{
		/*
		 * 如果没有 finalfn，聚合结果类型是状态值的类型
		 */
		fc_finaltype = fc_aggTransType;
	}
	Assert(OidIsValid(fc_finaltype));

	/* 处理提供的 combinefn */
	if (fc_aggcombinefnName)
	{
		Oid			fc_combineType;

		/*
		 * Combine 函数必须具有 2 个参数，每个参数都是 trans
		 * 类型。VARIADIC 不影响它。
		 */
		fc_fnArgs[0] = fc_aggTransType;
		fc_fnArgs[1] = fc_aggTransType;

		fc_combinefn = fc_lookup_agg_function(fc_aggcombinefnName, 2,
										fc_fnArgs, InvalidOid,
										&fc_combineType);

		/* 确保返回类型与聚合的 trans 类型匹配 */
		if (fc_combineType != fc_aggTransType)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("return type of combine function %s is not %s",
							NameListToString(fc_aggcombinefnName),
							format_type_be(fc_aggTransType))));

		
/*
		 * 一个合并函数用于合并内部状态必须接受 null，并且
		 * 确保返回的状态处于正确的内存上下文中。我们
		 * 不能直接检查后者，但我们可以检查前者。
		 */
		if (fc_aggTransType == INTERNALOID && func_strict(fc_combinefn))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("combine function with transition type %s must not be declared STRICT",
							format_type_be(fc_aggTransType))));
	}

	/*
	 * 验证序列化函数（如果存在）。
	 */
	if (fc_aggserialfnName)
	{
		/* 签名始终是 serialize(internal) 返回 bytea */
		fc_fnArgs[0] = INTERNALOID;

		fc_serialfn = fc_lookup_agg_function(fc_aggserialfnName, 1,
									   fc_fnArgs, InvalidOid,
									   &fc_rettype);

		if (fc_rettype != BYTEAOID)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("return type of serialization function %s is not %s",
							NameListToString(fc_aggserialfnName),
							format_type_be(BYTEAOID))));
	}

	/*
	 * 验证反序列化函数（如果存在）。
	 */
	if (fc_aggdeserialfnName)
	{
		/* 签名始终是 deserialize(bytea, internal) 返回 internal */
		fc_fnArgs[0] = BYTEAOID;
		fc_fnArgs[1] = INTERNALOID;	/* 为了类型安全的虚拟参数 */

		fc_deserialfn = fc_lookup_agg_function(fc_aggdeserialfnName, 2,
										 fc_fnArgs, InvalidOid,
										 &fc_rettype);

		if (fc_rettype != INTERNALOID)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("return type of deserialization function %s is not %s",
							NameListToString(fc_aggdeserialfnName),
							format_type_be(INTERNALOID))));
	}

	/*
	 * 如果 finaltype（即聚合返回类型）是多态的，输入也必须
	 * 是多态的，否则解析器将无法推导结果类型。
	 * （注意：根据之前对 transtype 和输入的测试，这种情况不能
	 * 发生，除非某人将 finalfn 定义偷偷放入目录中，而该定义
	 * 违反了多态结果没有多态输入的规则。）
	 */
	fc_detailmsg = check_valid_polymorphic_signature(fc_finaltype,
												  fc_aggArgTypes,
												  fc_numArgs);
	if (fc_detailmsg)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("cannot determine result data type"),
				 errdetail_internal("%s", fc_detailmsg)));

	/*
	 * 此外，返回类型不能是 INTERNAL，除非至少有一个
	 * INTERNAL 参数。这是我们为常规函数执行的相同类型安全限制，
	 * 但在聚合的层面。我们必须明确测试这一点，因为我们允许
	 * INTERNAL 作为 transtype。
	 */
	fc_detailmsg = check_valid_internal_signature(fc_finaltype,
											   fc_aggArgTypes,
											   fc_numArgs);
	if (fc_detailmsg)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("unsafe use of pseudo-type \"internal\""),
				 errdetail_internal("%s", fc_detailmsg)));

	/*
	 * 如果提供了移动聚合实现，查找其 finalfn（如果有），并检查
	 * 隐含的聚合结果类型是否与普通实现匹配。
	 */
	if (OidIsValid(fc_aggmTransType))
	{
		/* 处理提供的 finalfn */
		if (fc_aggmfinalfnName)
		{
			/*
			 * 参数的计算方式与常规 finalfn 相同，
			 * 但使用 aggmTransType 和 mfinalfnExtraArgs。
			 */
			Oid			fc_ffnVariadicArgType = fc_variadicArgType;

			fc_fnArgs[0] = fc_aggmTransType;
			memcpy(fc_fnArgs + 1, fc_aggArgTypes, fc_numArgs * sizeof(Oid));
			if (fc_mfinalfnExtraArgs)
				fc_nargs_finalfn = fc_numArgs + 1;
			else
			{
				fc_nargs_finalfn = fc_numDirectArgs + 1;
				if (fc_numDirectArgs < fc_numArgs)
				{
					/* 可变参数不影响 finalfn */
					fc_ffnVariadicArgType = InvalidOid;
				}
			}

			fc_mfinalfn = fc_lookup_agg_function(fc_aggmfinalfnName, fc_nargs_finalfn,
										   fc_fnArgs, fc_ffnVariadicArgType,
										   &fc_rettype);

			/* 如上所述，如果提供了 mfinalfnExtraArgs，则检查严格性 */
			if (fc_mfinalfnExtraArgs && func_strict(fc_mfinalfn))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("final function with extra arguments must not be declared STRICT")));
		}
		else
		{
			/*
			 * 如果没有 finalfn，聚合结果类型就是状态值的类型。
			 */
			fc_rettype = fc_aggmTransType;
		}
		Assert(OidIsValid(fc_rettype));
		if (fc_rettype != fc_finaltype)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("moving-aggregate implementation returns type %s, but plain implementation returns type %s",
							format_type_be(fc_rettype),
							format_type_be(fc_finaltype))));
	}

	/* 处理排序操作（如果提供） */
	if (fc_aggsortopName)
	{
		if (fc_numArgs != 1)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("sort operator can only be specified for single-argument aggregates")));
		fc_sortop = LookupOperName(NULL, fc_aggsortopName,
								fc_aggArgTypes[0], fc_aggArgTypes[0],
								false, -1);
	}

	/*
	 * 对使用的类型进行权限检查
	 */
	for (fc_i = 0; fc_i < fc_numArgs; fc_i++)
	{
		fc_aclresult = pg_type_aclcheck(fc_aggArgTypes[fc_i], GetUserId(), ACL_USAGE);
		if (fc_aclresult != ACLCHECK_OK)
			aclcheck_error_type(fc_aclresult, fc_aggArgTypes[fc_i]);
	}

	fc_aclresult = pg_type_aclcheck(fc_aggTransType, GetUserId(), ACL_USAGE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error_type(fc_aclresult, fc_aggTransType);

	if (OidIsValid(fc_aggmTransType))
	{
		fc_aclresult = pg_type_aclcheck(fc_aggmTransType, GetUserId(), ACL_USAGE);
		if (fc_aclresult != ACLCHECK_OK)
			aclcheck_error_type(fc_aclresult, fc_aggmTransType);
	}

	fc_aclresult = pg_type_aclcheck(fc_finaltype, GetUserId(), ACL_USAGE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error_type(fc_aclresult, fc_finaltype);


	/*
	 * 一切看起来都可以。尝试为聚合创建 pg_proc 条目。
	 * （如果已经存在冲突的条目，这可能会失败。）
	 */

	fc_myself = ProcedureCreate(fc_aggName,
							 fc_aggNamespace,
							 fc_replace,	/* 可能是替换 */
							 false, /* 不返回集合 */
							 fc_finaltype, /* returnType */
							 GetUserId(),	/* proowner */
							 INTERNALlanguageId,	/* languageObjectId */
							 InvalidOid,	/* 没有验证器 */
							 "aggregate_dummy", /* 占位符（没有这样的过程） */
							 NULL,	/* probin */
							 NULL,	/* prosqlbody */
							 PROKIND_AGGREGATE,
							 false, /* 安全调用者（目前不能为 agg 定义） */
							 false, /* isLeakProof */
							 false, /* isStrict（对 agg 不需要） */
							 PROVOLATILE_IMMUTABLE, /* volatility（对 agg 不需要） */
							 fc_proparallel,
							 fc_parameterTypes,	/* paramTypes */
							 fc_allParameterTypes, /* allParamTypes */
							 fc_parameterModes,	/* parameterModes */
							 fc_parameterNames,	/* parameterNames */
							 fc_parameterDefaults, /* parameterDefaults */
							 PointerGetDatum(NULL), /* trftypes */
							 PointerGetDatum(NULL), /* proconfig */
							 InvalidOid,	/* 没有 prosupport */
							 1, /* procost */
							 0);	/* prorows */
	fc_procOid = fc_myself.objectId;

	/*
	 * 可以创建 pg_aggregate 条目。
	 */
	fc_aggdesc = table_open(AggregateRelationId, RowExclusiveLock);
	fc_tupDesc = fc_aggdesc->rd_att;

	/* 初始化 nulls 和值 */
	for (fc_i = 0; fc_i < Natts_pg_aggregate; fc_i++)
	{
		fc_nulls[fc_i] = false;
		fc_values[fc_i] = (Datum) NULL;
		fc_replaces[fc_i] = true;
	}
	fc_values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(fc_procOid);
	fc_values[Anum_pg_aggregate_aggkind - 1] = CharGetDatum(fc_aggKind);
	fc_values[Anum_pg_aggregate_aggnumdirectargs - 1] = Int16GetDatum(fc_numDirectArgs);
	fc_values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(fc_transfn);
	fc_values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(fc_finalfn);
	fc_values[Anum_pg_aggregate_aggcombinefn - 1] = ObjectIdGetDatum(fc_combinefn);
	fc_values[Anum_pg_aggregate_aggserialfn - 1] = ObjectIdGetDatum(fc_serialfn);
	fc_values[Anum_pg_aggregate_aggdeserialfn - 1] = ObjectIdGetDatum(fc_deserialfn);
	fc_values[Anum_pg_aggregate_aggmtransfn - 1] = ObjectIdGetDatum(fc_mtransfn);
	fc_values[Anum_pg_aggregate_aggminvtransfn - 1] = ObjectIdGetDatum(fc_minvtransfn);
	fc_values[Anum_pg_aggregate_aggmfinalfn - 1] = ObjectIdGetDatum(fc_mfinalfn);
	fc_values[Anum_pg_aggregate_aggfinalextra - 1] = BoolGetDatum(fc_finalfnExtraArgs);
	fc_values[Anum_pg_aggregate_aggmfinalextra - 1] = BoolGetDatum(fc_mfinalfnExtraArgs);
	fc_values[Anum_pg_aggregate_aggfinalmodify - 1] = CharGetDatum(fc_finalfnModify);
	fc_values[Anum_pg_aggregate_aggmfinalmodify - 1] = CharGetDatum(fc_mfinalfnModify);
	fc_values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(fc_sortop);
	fc_values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(fc_aggTransType);
	fc_values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(fc_aggTransSpace);
	fc_values[Anum_pg_aggregate_aggmtranstype - 1] = ObjectIdGetDatum(fc_aggmTransType);
	fc_values[Anum_pg_aggregate_aggmtransspace - 1] = Int32GetDatum(fc_aggmTransSpace);
	if (fc_agginitval)
		fc_values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(fc_agginitval);
	else
		fc_nulls[Anum_pg_aggregate_agginitval - 1] = true;
	if (fc_aggminitval)
		fc_values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(fc_aggminitval);
	else
		fc_nulls[Anum_pg_aggregate_aggminitval - 1] = true;

	if (fc_replace)
		fc_oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(fc_procOid));
	else
		fc_oldtup = NULL;

	if (HeapTupleIsValid(fc_oldtup))
	{
		Form_pg_aggregate fc_oldagg = (Form_pg_aggregate) GETSTRUCT(fc_oldtup);

		/*
		 * 如果我们正在替换现有条目，则需要验证我们
		 * 不会更改任何会破坏调用者的内容。具体来说，
		 * 我们不能更改 aggkind 或 aggnumdirectargs，这会影响聚合
		 * 调用在解析分析中的处理方式。
		 */
		if (fc_aggKind != fc_oldagg->aggkind)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("cannot change routine kind"),
					 (fc_oldagg->aggkind == AGGKIND_NORMAL ?
					  errdetail("\"%s\" is an ordinary aggregate function.", fc_aggName) :
					  fc_oldagg->aggkind == AGGKIND_ORDERED_SET ?
					  errdetail("\"%s\" is an ordered-set aggregate.", fc_aggName) :
					  fc_oldagg->aggkind == AGGKIND_HYPOTHETICAL ?
					  errdetail("\"%s\" is a hypothetical-set aggregate.", fc_aggName) :
					  0)));
		if (fc_numDirectArgs != fc_oldagg->aggnumdirectargs)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("cannot change number of direct arguments of an aggregate function")));

		fc_replaces[Anum_pg_aggregate_aggfnoid - 1] = false;
		fc_replaces[Anum_pg_aggregate_aggkind - 1] = false;
		fc_replaces[Anum_pg_aggregate_aggnumdirectargs - 1] = false;

		fc_tup = heap_modify_tuple(fc_oldtup, fc_tupDesc, fc_values, fc_nulls, fc_replaces);
		CatalogTupleUpdate(fc_aggdesc, &fc_tup->t_self, fc_tup);
		ReleaseSysCache(fc_oldtup);
	}
	else
	{
		fc_tup = heap_form_tuple(fc_tupDesc, fc_values, fc_nulls);
		CatalogTupleInsert(fc_aggdesc, fc_tup);
	}

	table_close(fc_aggdesc, RowExclusiveLock);

	/*
	 * 为聚合创建依赖关系（超出由 ProcedureCreate 已经建立的依赖关系）。
	 * 注意：我们不需要对 aggTransType 进行显式依赖，
	 * 因为我们间接依赖它通过 transfn。
	 * 对于 aggmTransType 也是如此，使用 mtransfn，如果存在。
	 * 
	 * 如果我们正在替换现有的定义，ProcedureCreate 删除了所有
	 * 我们现有的依赖关系，因此我们在这里也必须做同样的事情。
	 */

	fc_addrs = new_object_addresses();

	/* 取决于转换函数 */  
	ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_transfn);
	add_exact_object_address(&fc_referenced, fc_addrs);

	/* 取决于最终函数（如果有的话） */  
	if (OidIsValid(fc_finalfn))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_finalfn);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 取决于合并函数（如果有的话） */  
	if (OidIsValid(fc_combinefn))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_combinefn);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 取决于序列化函数（如果有的话） */  
	if (OidIsValid(fc_serialfn))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_serialfn);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 取决于反序列化函数（如果有的话） */  
	if (OidIsValid(fc_deserialfn))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_deserialfn);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 取决于前向转换函数（如果有的话） */  
	if (OidIsValid(fc_mtransfn))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_mtransfn);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 取决于逆向转换函数（如果有的话） */  
	if (OidIsValid(fc_minvtransfn))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_minvtransfn);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 取决于最终函数（如果有的话） */  
	if (OidIsValid(fc_mfinalfn))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_mfinalfn);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 取决于排序运算符（如果有的话） */  
	if (OidIsValid(fc_sortop))
	{
		ObjectAddressSet(fc_referenced, OperatorRelationId, fc_sortop);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	record_object_address_dependencies(&fc_myself, fc_addrs, DEPENDENCY_NORMAL);
	free_object_addresses(fc_addrs);
	return fc_myself;
}

/*  
 * lookup_agg_function  
 * 查找聚合支持函数的通用代码  
 *  
 * fnName：可能带有模式限定的函数名称  
 * nargs，input_types：期望的函数参数类型  
 * variadicArgType：可变参数的类型（如果有），否则为InvalidOid  
 *  
 * 返回函数的OID，并将其返回类型存储到 *rettype  
 *  
 * 注：不得修改input_types[]，因为可能会重新使用这些  
 */  
static Oid fc_lookup_agg_function(List *fc_fnName,
					int fc_nargs,
					Oid *fc_input_types,
					Oid fc_variadicArgType,
					Oid *fc_rettype)
{
	Oid			fc_fnOid;
	bool		fc_retset;
	int			fc_nvargs;
	Oid			fc_vatype;
	Oid		   *fc_true_oid_array;
	FuncDetailCode fc_fdresult;
	AclResult	fc_aclresult;
	int			fc_i;

	/*  
	 * func_get_detail在目录中查找函数，进行多态函数的歧义处理，  
	 * 处理继承，并返回函数的funcid、类型以及返回值的集合或单例状态。  
	 * 还返回函数的真实参数类型。  
	 */  
	fc_fdresult = func_get_detail(fc_fnName, NIL, NIL,
							   fc_nargs, fc_input_types, false, false, false,
							   &fc_fnOid, fc_rettype, &fc_retset,
							   &fc_nvargs, &fc_vatype,
							   &fc_true_oid_array, NULL);

	/* 唯一有效的情况是一个正常函数不返回集合 */  
	if (fc_fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fc_fnOid))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("function %s does not exist",
						func_signature_string(fc_fnName, fc_nargs,
											  NIL, fc_input_types))));
	if (fc_retset)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("function %s returns a set",
						func_signature_string(fc_fnName, fc_nargs,
											  NIL, fc_input_types))));

	/*  
	 * 如果聚合被声明为接受VARIADIC ANY，底层函数也最好被声明为这样，  
	 * 否则它们可能会接收到过多的参数；但func_get_detail会满意地处理普通ANY。  
	 * （可能不会发生任何非常糟糕的事情，但它不会按用户预期工作。）  
	 * 其他组合应该可以在没有任何特殊处理的情况下工作，前提是我们告诉func_get_detail不要展开VARIADIC。  
	 */  
	if (fc_variadicArgType == ANYOID && fc_vatype != ANYOID)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("function %s must accept VARIADIC ANY to be used in this aggregate",
						func_signature_string(fc_fnName, fc_nargs,
											  NIL, fc_input_types))));

	/*  
	 * 如果涉及任何多态类型，强制一致性，并可能细化结果类型。  
	 * 不过，此时结果仍然是多态的也是可以的。  
	 */  
	*fc_rettype = enforce_generic_type_consistency(fc_input_types,
												fc_true_oid_array,
												fc_nargs,
												*fc_rettype,
												true);

	/*  
	 * func_get_detail会查找需要运行时参数类型强制转换的函数，  
	 * 但nodeAgg.c没有准备好处理这个问题。  
	 */  
	for (fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		if (!IsBinaryCoercible(fc_input_types[fc_i], fc_true_oid_array[fc_i]))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("function %s requires run-time type coercion",
							func_signature_string(fc_fnName, fc_nargs,
												  NIL, fc_true_oid_array))));
	}

	/* 检查聚合创建者是否有权限调用该函数 */  
	fc_aclresult = pg_proc_aclcheck(fc_fnOid, GetUserId(), ACL_EXECUTE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FUNCTION, get_func_name(fc_fnOid));

	return fc_fnOid;
}
